unit humanoids;

{*
Linked MD3 model of humanoid composed of legs, torso with arms, head, 2 swords and shield
}

interface

uses SysUtils, Classes, gl, md3s, vectors, VUMaths, models;

type
  THumanoid = class(TModel)
    // Body 
    Lower, Upper, Head : TMd3ModelDynamic;
    // Equipment
    RightWeapon, LeftWeapon, Shield : TMd3ModelDynamic; 
    // other
    EyesM : TMatrix4;
    Crouch : boolean;

    constructor Create(APath, ASkin : string); virtual;
    procedure Strafe(ALeft,ARight : boolean);
    procedure Move(AForward,ABack : boolean);

    function CreateEquipment(AParent : TMd3ModelDynamic; AParentTag, ABaseTag, APath, AModel, ASkin, AAnimation : string) : TMd3ModelDynamic; virtual;
    procedure CalculateEyesMatrix;
  end;

implementation

constructor THumanoid.Create(APath, ASkin : string);
{*
Create 3 body parts
}
begin
  inherited Create;
  // create models
  Lower := TMd3ModelDynamic.Create;
  Upper := TMd3ModelDynamic.Create;
  Head := TMd3ModelDynamic.Create;
  // load models
  Lower.ModelStatic := Md3ModelStaticList.Load(APath,'lower',ASkin);
  Upper.ModelStatic := Md3ModelStaticList.Load(APath,'upper',ASkin);
  Head.ModelStatic := Md3ModelStaticList.Load(APath,'head',ASkin);
  // link model
  Lower.LinkModel('tag_torso', Upper, 'tag_torso');
  Upper.LinkModel('tag_head', Head, 'tag_head');
  // first base tag
  Lower.Base('tag_ground');  // tag_ground
  // default animation
  Lower.SetAnimation('WALK');
  Upper.SetAnimation('ATTACK2');
  Head.SetAnimation('TALK');
  Speed := 0.5;
  // 
  BaseModel := Lower;
end;

function THumanoid.CreateEquipment(AParent : TMd3ModelDynamic; AParentTag, ABaseTag, APath, AModel, ASkin, AAnimation : string) : TMd3ModelDynamic; 
{*
Create equipment
}
begin 
  result := TMd3ModelDynamic.Create;   
  result.ModelStatic := Md3ModelStaticList.Load(APath, AModel, ASkin);
  result.SetAnimation(AAnimation);
  AParent.LinkModel(AParentTag,result,ABaseTag);
end;

procedure THumanoid.CalculateEyesMatrix;
var LowerBaseM, LowerTorsoM, UpperBaseM, UpperHeadM, HeadBaseM, HeadEyesM : TMatrix4;
begin
  // calculate eyes position
  // input parameters
  writeln('lower.BaseTag   = ',TagToStr(Lower.BaseTag));
  writeln('lower.tag_torso = ',TagToStr(Lower.ModelStatic.FindTag('tag_torso')));
  writeln('upper.BaseTag   = ',TagToStr(Upper.BaseTag));
  writeln('upper.tag_head  = ',TagToStr(Upper.ModelStatic.FindTag('tag_head')));
  writeln('head.BaseTag    = ',TagToStr(Head.BaseTag));
  writeln('head.tag_eyes   = ',TagToStr(Head.ModelStatic.FindTag('tag_eyes')));
  // create transformation matrices
  LowerBaseM :=  TagToMatrix4(Lower.BaseTag);
  LowerTorsoM := TagToMatrix4(Lower.ModelStatic.FindTag('tag_torso'));
  UpperBaseM :=  TagToMatrix4(Upper.BaseTag);
  UpperHeadM :=  TagToMatrix4(Upper.ModelStatic.FindTag('tag_head'));
  HeadBaseM :=   TagToMatrix4(Head.BaseTag);
  HeadEyesM :=   TagToMatrix4(Head.ModelStatic.FindTag('tag_eyes'));
  // calculate inverse matrices for base tags
  LowerBaseM := Mat4Inverse(LowerBaseM);  
  UpperBaseM := Mat4Inverse(UpperBaseM);  
  HeadBaseM := Mat4Inverse(HeadBaseM);  
  // lower - calculate final transformation
  EyesM := LowerBaseM;
  EyesM := Mat4Add(EyesM, LowerTorsoM);
  EyesM := Mat4Add(EyesM, UpperBaseM);
  EyesM := Mat4Add(EyesM, UpperHeadM);
  EyesM := Mat4Add(EyesM, HeadBaseM);
  EyesM := Mat4Add(EyesM, HeadEyesM);
  // lower - debug
  //Matrix4Debug(LowerBaseM,  'LowerBaseM');
  //Matrix4Debug(LowerTorsoM, 'LowerTorsoM');  
  Matrix4Debug(EyesM, 'EyesM');
end;

procedure THumanoid.Strafe(ALeft,ARight : boolean);
var k : integer;
begin
  k := 0;
  if ALeft then k := 1;
  if ARight then k := -1;
  if k <> 0 then
  begin
    Position.X := Position.X - k * Speed * Sin((Rotation.Z)/180*pi);
    Position.Y := Position.Y + k * Speed * Cos((Rotation.Z)/180*pi);
  end;
end;

procedure THumanoid.Move(AForward,ABack : boolean);
var k : integer;
begin
  k := 0;
  if AForward then k := 1;
  if ABack then k := -1;
  if k <> 0 then
  begin
    Position.X := Position.X - k * Speed * Sin((Rotation.Z-90)/180*pi);
    Position.Y := Position.Y + k * Speed * Cos((Rotation.Z-90)/180*pi);
  end;
end;

end.